/**********************************************************************

  Audacity: A Digital Audio Editor

  LogarithmicUpdater.cpp

  Dominic Mazzoni
  Michael Papadopoulos split from Ruler.cpp

**********************************************************************/

#include "LogarithmicUpdater.h"
#include "IntFormat.h"

const LogarithmicUpdater& LogarithmicUpdater::Instance()
{
    static LogarithmicUpdater instance;
    return instance;
}

void LogarithmicUpdater::Update(
    wxDC& dc, const Envelope* envelope,
    UpdateOutputs& allOutputs, const RulerStruct& context) const
{
    TickOutputs majorOutputs{
        allOutputs.majorLabels, allOutputs.bits, allOutputs.box };

    const int mLength = context.mLength;

    const int mOrientation = context.mOrientation;

    const double mMin = context.mMin;
    const double mMax = context.mMax;
    const double mHiddenMin = context.mHiddenMin;
    const double mHiddenMax = context.mHiddenMax;

    const RulerStruct::Fonts& mFonts = *context.mpFonts;
    const NumberScale mNumberScale = context.mNumberScale;

    auto numberScale = (mNumberScale == NumberScale{})
                       ? NumberScale(nstLogarithmic, mMin, mMax)
                       : mNumberScale;

    double UPP = (mHiddenMax - mHiddenMin) / mLength; // Units per pixel
    TickSizes tickSizes{ UPP, mOrientation, context.mpRulerFormat, true };

    tickSizes.mDigits = 2; //TODO: implement dynamic digit computation

    double loLog = log10(mMin);
    double hiLog = log10(mMax);
    int loDecade = (int)floor(loLog);

    double val;
    double startDecade = pow(10., (double)loDecade);

    // Major ticks are the decades
    double decade = startDecade;
    double delta = hiLog - loLog, steps = fabs(delta);
    double step = delta >= 0 ? 10 : 0.1;
    double rMin = std::min(mMin, mMax), rMax = std::max(mMin, mMax);
    for (int i = 0; i <= steps; i++) { // if(i!=0)
        {
            val = decade;
            if (val >= rMin && val < rMax) {
                const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
                Tick(dc, pos, val, tickSizes, mFonts.major, majorOutputs, context);
            }
        }
        decade *= step;
    }

    // Minor ticks are multiples of decades
    decade = startDecade;
    float start, end, mstep;
    if (delta > 0) {
        start = 2;
        end = 10;
        mstep = 1;
    } else {
        start = 9;
        end = 1;
        mstep = -1;
    }
    steps++;
    tickSizes.tickType = RulerFormat::t_minor;
    TickOutputs minorOutputs{
        allOutputs.minorLabels, allOutputs.bits, allOutputs.box };
    for (int i = 0; i <= steps; i++) {
        for (int j = start; j != end; j += mstep) {
            val = decade * j;
            if (val >= rMin && val < rMax) {
                const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
                Tick(dc, pos, val, tickSizes, mFonts.minor, minorOutputs, context);
            }
        }
        decade *= step;
    }

    // MinorMinor ticks are multiples of decades
    decade = startDecade;
    if (delta > 0) {
        start = 10;
        end = 100;
        mstep = 1;
    } else {
        start = 100;
        end = 10;
        mstep = -1;
    }
    steps++;
    tickSizes.tickType = RulerFormat::t_minorMinor;
    TickOutputs minorMinorOutputs{
        allOutputs.minorMinorLabels, allOutputs.bits, allOutputs.box };
    for (int i = 0; i <= steps; i++) {
        // PRL:  Bug1038.  Don't label 1.6, rounded, as a duplicate tick for "2"
        if (!(context.mpRulerFormat == &IntFormat::Instance() && decade < 10.0)) {
            for (int f = start; f != (int)(end); f += mstep) {
                if ((int)(f / 10) != f / 10.0f) {
                    val = decade * f / 10;
                    if (val >= rMin && val < rMax) {
                        const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
                        Tick(dc, pos, val, tickSizes,
                             mFonts.minorMinor, minorMinorOutputs, context);
                    }
                }
            }
        }
        decade *= step;
    }

    BoxAdjust(allOutputs, context);
}

LogarithmicUpdater::~LogarithmicUpdater() = default;
